import xss from 'pretty-text/xss';

const _validIframes = [];

function attr(name, value) {
  if (value) {
    return `${name}="${xss.escapeAttrValue(value)}"`;
  }

  return name;
}

const ESCAPE_REPLACEMENTS = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#x27;",
  '`': '&#x60;'
};
const BAD_CHARS = /[&<>"'`]/g;
const POSSIBLE_CHARS = /[&<>"'`]/;

function escapeChar(chr) {
  return ESCAPE_REPLACEMENTS[chr];
}

export function escape(string) {
  // don't escape SafeStrings, since they're already safe
  if (string === null) {
    return "";
  } else if (!string) {
    return string + '';
  }

  // Force a string conversion as this will be done by the append regardless and
  // the regex test will do this transparently behind the scenes, causing issues if
  // an object's to string has escaped characters in it.
  string = "" + string;

  if (!POSSIBLE_CHARS.test(string)) { return string; }
  return string.replace(BAD_CHARS, escapeChar);
}

export function hrefAllowed(href, extraHrefMatchers) {
  // escape single quotes
  href = href.replace(/'/g, "%27");

  // absolute urls
  if (/^(https?:)?\/\/[\w\.\-]+/i.test(href)) { return href; }
  // relative urls
  if (/^\/[\w\.\-]+/i.test(href)) { return href; }
  // anchors
  if (/^#[\w\.\-]+/i.test(href)) { return href; }
  // mailtos
  if (/^mailto:[\w\.\-@]+/i.test(href)) { return href; }

  if (extraHrefMatchers && extraHrefMatchers.length > 0) {
    for (let i=0; i<extraHrefMatchers.length; i++) {
      if (extraHrefMatchers[i].test(href)) { return href; }
    }
  }
}

export function sanitize(text, whiteLister) {
  if (!text) return "";

  // Allow things like <3 and <_<
  text = text.replace(/<([^A-Za-z\/\!]|$)/g, "&lt;$1");

  const whiteList = whiteLister.getWhiteList(),
        allowedHrefSchemes = whiteLister.getAllowedHrefSchemes();
  let extraHrefMatchers = null;

  if (allowedHrefSchemes && allowedHrefSchemes.length > 0) {
    extraHrefMatchers = [new RegExp('^(' + allowedHrefSchemes.join('|') + '):\/\/[\\w\\.\\-]+','i')];
  }

  let result = xss(text, {
    whiteList: whiteList.tagList,
    stripIgnoreTag: true,
    stripIgnoreTagBody: ['script', 'table'],

    onIgnoreTagAttr(tag, name, value) {
      const forTag = whiteList.attrList[tag];
      if (forTag) {
        const forAttr = forTag[name];
        if ((forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) ||
            (name.indexOf('data-') === 0 && forTag['data-*']) ||
            ((tag === 'a' && name === 'href') && hrefAllowed(value, extraHrefMatchers)) ||
            (tag === 'img' && name === 'src' && (/^data:image.*$/i.test(value) || hrefAllowed(value, extraHrefMatchers))) ||
            (tag === 'iframe' && name === 'src' && _validIframes.some(i => i.test(value)))) {
          return attr(name, value);
        }

        if (tag === 'iframe' && name === 'src') {
          return "-STRIP-";
        }

        const custom = whiteLister.getCustom();
        for (let i=0; i<custom.length; i++) {
          const fn = custom[i];
          if (fn(tag, name, value)) {
            return attr(name, value);
          }
        }
      }
    },
  });

  return result.replace(/\[removed\]/g, '')
               .replace(/\<iframe[^>]+\-STRIP\-[^>]*>[^<]*<\/iframe>/g, '')
               .replace(/&(?![#\w]+;)/g, '&amp;')
               .replace(/&#39;/g, "'")
               .replace(/ \/>/g, '>');
};

export function whiteListIframe(regexp) {
  _validIframes.push(regexp);
}

whiteListIframe(/^(https?:)?\/\/www\.google\.com\/maps\/embed\?.+/i);
whiteListIframe(/^(https?:)?\/\/www\.openstreetmap\.org\/export\/embed.html\?.+/i);
